package com.uuah.server.config.spring.parsers.specific;

import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.uuah.server.config.spring.parsers.AbstractUuahBeanDefinitionParser;
import com.uuah.server.config.spring.parsers.IUuahDefinitionParser;
import com.uuah.server.config.spring.parsers.PreProcessor;
import com.uuah.server.config.spring.parsers.assembly.configuration.IPropertyConfiguration;
import com.uuah.server.config.spring.parsers.delegate.AbstractParallelDelegatingDefinitionParser;
import com.uuah.server.util.spring.SpringXMLUtils;
import com.uuah.utils.StringUtils;

/**
 * Allows for parsing either a shortcut component configuration by delegating to
 * two different component parses depending on the existence of the class
 * attribute. If the class attribute is used then an embedded object factory
 * element cannot be used.
 * 
 * <pre>
 * &lt;component class=&quot;&quot;&gt;
 * </pre>
 * 
 * or one with an embedded object factory element.
 * 
 * <pre>
 * &lt;component&gt;
 *     &lt;singleon-object class=&quot;..&quot;/&gt;
 * &lt;/component&gt;
 * </pre>
 */
public class ComponentDelegatingDefinitionParser extends
		AbstractParallelDelegatingDefinitionParser {

	private IUuahDefinitionParser normalConfig;
	private IUuahDefinitionParser shortcutConfig;

	public ComponentDelegatingDefinitionParser(Class clazz) {
		normalConfig = new ComponentDefinitionParser(clazz);
		shortcutConfig = new ShortcutComponentDefinitionParser(clazz);
		addDelegate(normalConfig);
		addDelegate(shortcutConfig);
		registerPreProcessor(new CheckExclusiveClassAttributeObjectFactory());
	}

	protected IUuahDefinitionParser getDelegate(Element element,
			ParserContext parserContext) {
		if (StringUtils.isEmpty(element
				.getAttribute(AbstractUuahBeanDefinitionParser.ATTRIBUTE_CLASS))) {
			return normalConfig;
		} else {
			return shortcutConfig;
		}
	}

	/**
	 * Given that the service object-factory is extensible and new object
	 * factory types can be implemented and used by substitution, the only
	 * extensible way of checking for the existence of an object-factory child
	 * element is by exclusion.<br>
	 * This pre-processor checks for the existence of a <i>"class"</i> attribute
	 * on the service, and throws an exception if the service has any child
	 * elements that are not binding's.
	 */
	class CheckExclusiveClassAttributeObjectFactory implements PreProcessor {

		private static final String POOLING_PROFILE_CHILD_ELEMENT = "pooling-profile";
		private static final String LIFECYCLE_ADAPTER_FACTORT_CHILD_ELEMENT = "lifecycle-adapter-factory";
		private static final String ENTRY_POINT_RESOLVER_CHILD_ELEMENT = "entry-point-resolver";
		private static final String ENTRY_POINT_RESOLVER_SET_CHILD_ELEMENT = "entry-point-resolver-set";

		public void preProcess(IPropertyConfiguration config, Element element) {
			NamedNodeMap attributes = element.getAttributes();
			for (int i = 0; i < attributes.getLength(); i++) {
				String alias = SpringXMLUtils.attributeName((Attr) attributes
						.item(i));
				if (alias
						.equals(AbstractUuahBeanDefinitionParser.ATTRIBUTE_CLASS)) {
					for (int j = 0; j < element.getChildNodes().getLength(); j++) {
						Node child = element.getChildNodes().item(j);
						if (child instanceof Element
								&& !(child.getLocalName().equals(
										POOLING_PROFILE_CHILD_ELEMENT)
										|| child
												.getLocalName()
												.equals(
														ENTRY_POINT_RESOLVER_CHILD_ELEMENT)
										|| child
												.getLocalName()
												.equals(
														ENTRY_POINT_RESOLVER_SET_CHILD_ELEMENT) || child
										.getLocalName()
										.endsWith(
												LIFECYCLE_ADAPTER_FACTORT_CHILD_ELEMENT))) {
							StringBuffer message = new StringBuffer(
									"The child element '");
							message.append(child.getLocalName());
							message
									.append("' cannot appear with the 'class' attribute");
							message.append(" in element ");
							message.append(SpringXMLUtils
									.elementToString(element));
							message.append(".");
							throw new CheckExclusiveClassAttributeObjectFactoryException(
									message.toString());
						}
					}

				}
			}
		}
	}

	class CheckExclusiveClassAttributeObjectFactoryException extends
			IllegalStateException {
		private static final long serialVersionUID = 4625276914151932111L;

		private CheckExclusiveClassAttributeObjectFactoryException(
				String message) {
			super(message);
		}
	}

}
